﻿using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

namespace NFox.Runtime.Com
{
    public static class Utils
    {
        public static RegistryKey ClassRoot { get; }
            = Registry.ClassesRoot;

        public static RegistryKey ComponentCategories { get; }
            = ClassRoot.OpenSubKey("Component Categories");

        public static RegistryKey ClsIds { get; }
            = ClassRoot.OpenSubKey("CLSID");

        public static RegistryKey AppIds { get; }
            = ClassRoot.OpenSubKey("AppId");

        public static RegistryKey Interfaces { get; }
            = ClassRoot.OpenSubKey("Interface");

        public static RegistryKey TypeLibs { get; }
            = ClassRoot.OpenSubKey("TypeLib");

        /// <summary>
        /// Com任务内存分配事务
        /// </summary>
        public class IPTrans : IDisposable
        {
            private List<IntPtr> _ips = new List<IntPtr>();

            /// <summary>
            /// 分配指定大小的内存
            /// </summary>
            /// <param name="size">要分配的内存块的大小</param>
            /// <returns>指向内存块的指针</returns>
            public IntPtr Alloc(int size)
            {
                var ip = Marshal.AllocCoTaskMem(size);
                _ips.Add(ip);
                return ip;
            }

            public IntPtr Alloc<T>()
            {
                return Alloc(Marshal.SizeOf(typeof(T)));
            }

            public void Dispose()
            {
                foreach (var ip in _ips)
                    Marshal.FreeCoTaskMem(ip);
            }
        }

        /// <summary>
        /// 从非托管内存中获取指定类型的托管对象
        /// </summary>
        /// <typeparam name="T">托管对象的类型</typeparam>
        /// <param name="ip">指向非托管内存的指针</param>
        /// <returns>指定类型的托管对象</returns>
        public static T GetObject<T>(IntPtr ip)
        {
            return (T)Marshal.PtrToStructure(ip, typeof(T));
        }

        /// <summary>
        /// 从非托管内存中获取指定类型的托管对象数组
        /// </summary>
        /// <typeparam name="T">托管对象的类型</typeparam>
        /// <param name="ip">指向非托管内存的指针</param>
        /// <param name="num">数组大小</param>
        /// <returns>指定类型的托管对象数组</returns>
        public static T[] GetObjects<T>(IntPtr ip, int num)
        {
            if (ip == IntPtr.Zero)
                return new T[0];
            int size = Marshal.SizeOf(typeof(T));
            T[] arr = new T[num];
            for (int i = 0; i < num; i++)
            {
                arr[i] = GetObject<T>(ip);
                ip += size;
            }
            return arr;
        }
    }
}